package com.autumn.log.client.logback;

import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

import com.autumn.log.LogEventData;
import com.autumn.log.LogEventDataMessage;
import com.autumn.mq.event.IMqEventBus;
import com.autumn.util.EnvironmentConstants;
import com.autumn.util.NetworkUtils;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;

/**
 * 短信客户端
 * 
 * @author 老码农
 *
 *         2018-02-01 12:47:49
 */
public class AutumnLoggingClient {

	/**
	 * 日志 Bean 名称
	 */
	public final static String LOGGING_BEAN_NAME = "autumnLoggingClient";

	private final IMqEventBus mqEventBus;

	/**
	 * 服务id
	 */
	private final String serviceId;
	private final String serviceAddress;
	private final String pid;
	private final int bufferSize;
	private final Queue<LogEventDataMessage> logQuery = new LinkedBlockingQueue<>();
	private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

	/**
	 * 
	 * @param mqEventBus
	 * @param serviceId
	 */
	public AutumnLoggingClient(IMqEventBus mqEventBus, String serviceId) {
		this.mqEventBus = mqEventBus;
		this.serviceId = serviceId;
		this.serviceAddress = NetworkUtils.getHostAddress();
		this.bufferSize = 50;
		this.pid = ManagementFactory.getRuntimeMXBean().getName().split("\\@")[0];
		// 防止同一实例未注入
		mqEventBus.getFactory().register(null, new String[] { LogEventData.class.getPackage().getName() });
	}

	private LogEventDataMessage createEventDataMessage(ILoggingEvent loggingEvent, String clientAddress) {
		LogEventDataMessage log = new LogEventDataMessage();
		log.setPid(this.pid);
		log.setClientAddress(clientAddress);
		log.setLevel(loggingEvent.getLevel().toString());
		log.setServiceLogTime(loggingEvent.getTimeStamp());
		log.setThreadName(loggingEvent.getThreadName());
		log.setLoggerName(loggingEvent.getLoggerName());
		StringBuilder sb = new StringBuilder();
		if (loggingEvent.getFormattedMessage() != null) {
			sb.append(loggingEvent.getFormattedMessage());
		}
		if (loggingEvent.getThrowableProxy() != null) {
			sb.append(EnvironmentConstants.LINE_SEPARATOR);
			sb.append(ThrowableProxyUtil.asString(loggingEvent.getThrowableProxy()));
		}
		log.setMessage(sb.toString());
		return log;
	}

	/**
	 * 发送
	 * 
	 * @param loggingEvent
	 *            日志事件
	 * @param clientAddress
	 *            地址
	 *
	 */
	public void send(ILoggingEvent loggingEvent, String clientAddress) {
		LogEventDataMessage log = createEventDataMessage(loggingEvent, clientAddress);
		logQuery.offer(log);
		execute();
	}

	/**
	 * 发送
	 * 
	 * @param loggingEvents
	 *            事件集合
	 *
	 */
	public void send(Collection<ILoggingEvent> loggingEvents) {
		for (ILoggingEvent event : loggingEvents) {
			LogEventDataMessage log = createEventDataMessage(event, null);
			logQuery.offer(log);
		}
		execute();
	}

	/**
	 * 执行
	 * 
	 *
	 */
	private void execute() {
		singleThreadExecutor.execute(new Runnable() {
			@Override
			public void run() {
				flush();
			}
		});
	}

	/**
	 * 写入
	 * 
	 *
	 */
	private void flush() {
		try {
			List<LogEventDataMessage> logList = new ArrayList<>();
			LogEventDataMessage logMessage;
			while (logList.size() < this.bufferSize && (logMessage = logQuery.poll()) != null) {
				logList.add(logMessage);
			}
			if (logList.size() > 0) {
				LogEventData logData = new LogEventData();
				logData.setServiceId(this.serviceId);
				logData.setServiceAddress(this.serviceAddress);
				logData.setMessages(logList);
				this.mqEventBus.send(logData);
			}
		} catch (IllegalStateException ex) {
			// 上下文已关闭
		} catch (Exception ex) {
			ex.printStackTrace(System.err);
		} finally {
			if (logQuery.size() > 0) {
				execute();
			}
		}
	}
}
